﻿/* 
 * Evo
 * 
 * Marcin Krupowicz (marcin@krupowicz.eu)
 * Barbara Szymanek (basia.szymanek@gmail.com)
 * 
 * January, 2009
 *
 */

using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;

namespace Evo
{
	#region chromosome types
	/// <summary>
	/// Abstract class for type of chromosome
	/// </summary>
	public abstract class ChromosomeType : IEnumerable
	{
		/// <summary>
		/// Length of chromosome
		/// </summary>
		public abstract int Length { get; }
		//public abstract int this[int index] { get; set; }

		#region IEnumerable Members
		IEnumerator IEnumerable.GetEnumerator() { return null; }
		#endregion
	}

	/// <summary>
	/// Chromosome composed of vector of integers
	/// </summary>
	public class IntegerChromosome : ChromosomeType, IEnumerable
	{
		private int[] genes;

		/// <summary>
		/// Length of chromosome
		/// </summary>
		public override int Length
		{
			get { return genes.Length; }
		}

		public IntegerChromosome()
		{
		}

		/// <summary>
		/// Create chromosome of given size, with all genes 0
		/// </summary>
		/// <param name="size">size of chromosome</param>
		public IntegerChromosome(int size)
		{
			this.genes = new int[size];
		}

		/// <summary>
		/// Create chromosome composed of given genes
		/// </summary>
		/// <param name="genes">vector of genes</param>
		public IntegerChromosome(int[] genes)
		{
			this.genes = genes;
		}

		/// <summary>
		/// Get/Set gen at given index
		/// </summary>
		/// <param name="index">index of gene</param>
		/// <returns>gene</returns>
		public int this[int index]
		{
			get { return genes[index]; }
			set { genes[index] = value; }
		}

		#region IEnumerable Members

		IEnumerator IEnumerable.GetEnumerator()
		{
			return genes.GetEnumerator();
		}

		#endregion

	}

	/// <summary>
	/// Chromosome composed of vector of real numbers
	/// </summary>
	public class RealChromosome : ChromosomeType, IEnumerable
	{
		private double[] genes;

		/// <summary>
		/// Length of chromosome
		/// </summary>
		public override int Length
		{
			get { return genes.Length; }
		}

		public RealChromosome()
		{
		}

		/// <summary>
		/// Create chromosome of given size, with all genes 0.0
		/// </summary>
		/// <param name="size">size of chromosome</param>
		public RealChromosome(int size)
		{
			this.genes = new double[size];
		}

		/// <summary>
		/// Create chromosome composed of given genes
		/// </summary>
		/// <param name="genes">vector of genes</param>
		public RealChromosome(double[] genes)
		{
			this.genes = genes;
		}

		/// <summary>
		/// Get/Set gen at given index
		/// </summary>
		/// <param name="index">index of gene</param>
		/// <returns>gene</returns>
		public double this[int index]
		{
			get { return genes[index]; }
			set { genes[index] = value; }
		}

		#region IEnumerable Members

		IEnumerator IEnumerable.GetEnumerator()
		{
			return genes.GetEnumerator();
		}

		#endregion
	}

	/// <summary>
	/// Chromosome composed of vector of numbers 0 and 1 (TODO: to be implemented)
	/// </summary>
	public class BinaryChromosome : ChromosomeType
	{
		public override int Length
		{
			get { return 0; }
		}
	}
	#endregion

	#region individual interface
	/// <summary>
	/// Interface for individuals
	/// </summary>
	public interface IIndividual : ICloneable
	{
		ChromosomeType Chromosome { get; }
		int ChromosomeLength { get; }
		double Value { get; }
		double Fitness { get; set; }
        EvaluateChromosomeDelegate ObjectiveFunction { get; set; }
		void Evaluate();
		void SetRandomChromosome(int chromosomeLength);
		new Object Clone();
		string ToString();
    }
    #endregion

    #region abstract individual
	/// <summary>
	/// Abstract class of Individual
	/// </summary>
    public abstract class Individual : IIndividual
	{
		#region Protected
		protected static Random random = new Random(DateTime.Now.Millisecond);
		protected ChromosomeType chromosome;
		protected double? value = null;
		protected double fitness = 0.0;
        protected EvaluateChromosomeDelegate objectiveFunction;
		#endregion

		#region Properties
		/// <summary>
		/// Get chromosome
		/// </summary>
		public ChromosomeType Chromosome
		{
			get { return chromosome; }
		}

		/// <summary>
		/// Get length of chromosome
		/// </summary>
		public int ChromosomeLength
		{
			get { return chromosome.Length; }
		}

		/// <summary>
		/// Get value of objective function
		/// </summary>
		public double Value
		{
			get 
            {
                //jak nie mamy to liczymy
                if (value == null)
                    this.value = objectiveFunction(this);
                
                return value.Value;
            }
		}

		/// <summary>
		/// Get/set fitness value. Set should be used carefully, rather not by end user
		/// </summary>
		public double Fitness
		{
			get { return fitness; }
			set { fitness = value; }
		}

		/// <summary>
		/// Get/set objective function
		/// </summary>
        public EvaluateChromosomeDelegate ObjectiveFunction
        {
            get { return objectiveFunction; }
            set { objectiveFunction = value; }
        }
		#endregion

		public Individual() { }

		/// <summary>
		/// Create individual with given chromosome
		/// </summary>
		/// <param name="chromosome">chromosome</param>
		public Individual(ChromosomeType chromosome)
		{
			this.chromosome = chromosome;
		}

		/// <summary>
		/// Evaluate individual
		/// </summary>
		public void Evaluate()
		{
            this.value = objectiveFunction(this);
		}

		/// <summary>
		/// Set random chromosome
		/// </summary>
		/// <param name="length">Length of chromosome</param>
		public abstract void SetRandomChromosome(int length);

		public abstract Object Clone();

		public override string ToString()
		{
			StringBuilder sb = new StringBuilder();
			foreach (Object gen in this.chromosome)
			{
				sb.Append(gen.ToString());
				sb.Append(" ");
			}
			return sb.ToString();
		}
    }
    #endregion

    #region PermutationIndividual
	/// <summary>
	/// Represents individual with permutational chromosome. Values of chromosome are 0, 1, 2, ..., n
	/// </summary>
    public class PermutationIndividual : Individual, IComparable<PermutationIndividual>
	{
		/// <summary>
		/// Create new individual with not specified length of chromosome and any other parameter
		/// </summary>
        public PermutationIndividual()
            : base() { }

		/// <summary>
		/// Create individual with given chromosome
		/// </summary>
		/// <param name="chromosome">chromosome</param>
        public PermutationIndividual(ChromosomeType chromosome)
            : base(chromosome)
        { }

		/// <summary>
		/// Set random chromosome
		/// </summary>
		/// <param name="length">length of chromosome</param>
		public override void SetRandomChromosome(int length)
		{
			List<int> inputList = new List<int>(length);
			for (int i = 0; i < length; i++)
				inputList.Add(i);

            IntegerChromosome chromosome = new IntegerChromosome(length);
			for (int i = 0; i < length; i++)
			{
				int index = random.Next(0, length - i);
				chromosome[i] = inputList[index];
				inputList.RemoveAt(index);
			}
			this.chromosome = chromosome;
		}

		public override Object Clone()
		{

			PermutationIndividual individual = new PermutationIndividual();
			IntegerChromosome thisChromosome = (IntegerChromosome)this.chromosome;
			IntegerChromosome newChromosome = new IntegerChromosome(this.chromosome.Length);
            for (int i = 0; i < this.chromosome.Length; i++)
                newChromosome[i] = thisChromosome[i];

			individual.chromosome = newChromosome;
			individual.fitness = this.fitness;
			individual.value = this.value;
            individual.objectiveFunction = this.objectiveFunction;
			return individual;
		}

		public int CompareTo(PermutationIndividual ind)
		{
			return -Value.CompareTo(ind.Value);
		}
    }
    #endregion

	#region RealIndividual 
	/// <summary>
	/// Represent individual with chromosome composed of real numbers
	/// </summary>
	public class RealIndividual : Individual, IComparable<RealIndividual>
	{
		/// <summary>
		/// Create new individual with not specified length of chromosome and any other parameter
		/// </summary>
		public RealIndividual()
			: base()
		{ 
		}

		/// <summary>
		/// Create individual with given chromosome
		/// </summary>
		/// <param name="chromosome">chromosome</param>
		public RealIndividual(ChromosomeType chromosome)
			: base(chromosome)
		{
		}

		/// <summary>
		/// Generate random chromosome. Each gene is real number from range [0, 1]
		/// </summary>
		/// <param name="length">Length of chromosome</param>
		public override void SetRandomChromosome(int length)
		{
			RealChromosome chromosome = new RealChromosome(length);
			for (int i = 0; i < length; i++)
				chromosome[i] = random.NextDouble();
			this.chromosome = chromosome;
		}

		public override Object Clone()
		{

			RealIndividual individual = new RealIndividual();
			RealChromosome thisChromosome = (RealChromosome)this.chromosome;
			RealChromosome newChromosome = new RealChromosome(this.chromosome.Length);
			for (int i = 0; i < this.chromosome.Length; i++)
				newChromosome[i] = thisChromosome[i];

			individual.chromosome = newChromosome;
			individual.fitness = this.fitness;
			individual.value = this.value;
			individual.objectiveFunction = this.objectiveFunction;
			return individual;
		}

		public int CompareTo(RealIndividual ind)
		{
			return -Value.CompareTo(ind.Value);
		}
	}

	#endregion

	#region BinaryIndividual
	/// <summary>
	/// Represent individual with chromosome composed of real numbers (TODO: to be implemented)
	/// </summary>
	public class BinaryIndividual : Individual, IComparable<BinaryIndividual>
	{
		public BinaryIndividual()
		{
			throw new NotImplementedException();
		}

		public override void SetRandomChromosome(int length)
		{
			throw new NotImplementedException();
		}

		public override Object Clone()
		{
			throw new NotImplementedException();
		}

		public int CompareTo(BinaryIndividual ind)
		{
			//return -Value.CompareTo(ind.Value);
			throw new NotImplementedException();
		}
	}
    #endregion
}
